﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.OMM.Util;
using Sinacor.OMM.ConnectionManager.ErrorHandlers;
using System.Messaging;

namespace Sinacor.OMM.ConnectionManager.MSMQ
{
    /// <summary>
    /// Creates and manages a connection with a message queue.
    /// </summary>
    public class MsmqReceiver : IDisposable
    {
        #region Constants
        private const string TRACE_FOLDER_NAME = "ConnectionManager";
        #endregion

        #region Attributes
        private PSE.Framework.Messaging.MsmqReceiver _receiver;
        private string _traceFileName;
        private string _traceFileNameHealthStatus;
        private TraceWriter _traceWriter;
        private TraceWriter _traceWriterHealthStatus;
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the last error code that has occurred.
        /// </summary>
        public int LastErrorCode
        {
            get { return _receiver.LastErrorCode; }
        }

        /// <summary>
        /// Gets or sets the last error message that has occurred.
        /// </summary>
        public string LastErrorMessage
        {
            get { return _receiver.LastErrorMessage; }
        }

        /// <summary>
        /// Gets or sets the queue's path.
        /// </summary>
        public string Path
        {
            get { return _receiver.Path; }
        }

        /// <summary>
        /// Gets the size in bytes of the queue.
        /// </summary>
        public long QueueSizeBytes
        {
            get { return _receiver.QueueSizeBytes; }
        }

        /// <summary>
        /// Gets the number of messages in the queue.
        /// </summary>
        public int QueuedMessages
        {
            get { return _receiver.QueuedMessages; }
        }

        /// <summary>
        /// Gets the number of messages received per second by the MsmqReceiver object.
        /// </summary>
        public int MessagesReceivePerSecond
        {
            get { return _receiver.MessagesReceivePerSecond; }
        }

        /// <summary>
        /// Gets a value indicating whether the MsmqReceiver is connected to a queue.
        /// </summary>
        public bool IsConnected
        {
            get { return _receiver.IsConnected; }
        }

        /// <summary>
        /// Gets the machine's name of the queue's path.
        /// </summary>
        public string MachineName
        {
            get { return _receiver.MachineName; }
        }

        /// <summary>
        /// Gets the queue's name of the queue's path.
        /// </summary>
        public string QueueName
        {
            get { return _receiver.QueueName; }
        }

        /// <summary>
        /// Gets the format's name of the queue's path.
        /// </summary>
        public string FormatName
        {
            get { return _receiver.FormatName; }
        }

        /// <summary>
        /// Gets total number of connection tries.
        /// </summary>
        public long TotalMsmqConnectionTries
        {
            get { return _receiver.TotalMsmqConnectionTries; }
        }

        #endregion

        #region Delegates
        /// <summary>
        /// <para>Represents the method that will handle the Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver.OnHealthStatusChanged event</para>
        /// of a Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver object.
        /// </summary>
        /// <param name="msmqReceiver"></param>
        public delegate void HealthStatusChanged(MsmqReceiver msmqReceiver);
        /// <summary>
        /// Represents the method that will deliver the message.
        /// </summary>
        /// <param name="msg">The System.Messaging.Message object to be delivered.</param>        
        public delegate void MessageArrival(Message msg, PSE.Framework.Messaging.MessageReceivedEventArgs e);
        #endregion

        #region Events
        /// <summary>
        /// Occurs when the Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver object changed its state.
        /// </summary>
        public event HealthStatusChanged OnHealthStatusChanged;
        /// <summary>
        /// Occurs when the Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver received a message.
        /// </summary>
        public event MessageArrival MessageReceived;
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the PSE.Framework.Messaging.MsmqReceiver class.
        /// </summary>
        /// <param name="path">The path of the message queue.</param>        
        public MsmqReceiver(string path)
        {
            _traceFileName = "MsmqReceiver " + path.Substring(path.LastIndexOf("\\") + 1);
            _traceFileNameHealthStatus = "MsmqReceiver HealthStatus " + path.Substring(path.LastIndexOf("\\") + 1);

            _traceWriter = Tracer.GetInstance(TRACE_FOLDER_NAME, _traceFileName);
            _traceWriterHealthStatus = Tracer.GetInstance(TRACE_FOLDER_NAME, _traceFileNameHealthStatus);

            _receiver = new PSE.Framework.Messaging.MsmqReceiver(path);
            _receiver.MessageReceived += new PSE.Framework.Messaging.MsmqReceiver.MessageArrival(_receiver_MessageReceived);
            _receiver.Connected += new EventHandler(_receiver_Connected);
            _receiver.Disconnected += new EventHandler(_receiver_Disconnected);
            _receiver.Error += new PSE.Framework.Messaging.ErrorEventHandler(_receiver_Error);
            _receiver.OnHealthStatusChanged += new PSE.Framework.Messaging.MsmqReceiver.HealthStatusChanged(_receiver_OnHealthStatusChanged);
            _receiver.Paused += new EventHandler(_receiver_Paused);
            _receiver.Resumed += new EventHandler(_receiver_Resumed);

            _traceWriter.Trace("Queue Path.........: " + _receiver.Path);
            _traceWriter.Trace("The MsmqReceiver iniciou com sucesso.");
        }        
        
        #endregion

        #region Methods

        #region _receiver object

        void _receiver_MessageReceived(Message msg, PSE.Framework.Messaging.MessageReceivedEventArgs e)
        {
            if (MessageReceived != null)            
                MessageReceived(msg, e);
        }

        private void _receiver_OnHealthStatusChanged(PSE.Framework.Messaging.MsmqReceiver msmqReceiver)
        {
            _traceWriterHealthStatus.Trace("Tamanho da fila....: [" + msmqReceiver.QueueSizeBytes + "] bytes.",TraceType.Optional);
            _traceWriterHealthStatus.Trace("Mensagens na fila..: [" + msmqReceiver.QueuedMessages + "].", TraceType.Optional);

            if (OnHealthStatusChanged != null)
                OnHealthStatusChanged(this);
        }

        private void _receiver_Error(object sender, PSE.Framework.Messaging.ErrorEventArgs e)
        {
            ErrorHandler errorHandler = new ErrorHandler();
            errorHandler.Exception = e.Exception;
            errorHandler.Msmq = (PSE.Framework.Messaging.MsmqReceiver)sender;

            switch (e.ErrorCode)
            {
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_CONNECTION_FAILURE:
                    errorHandler.Handler = new ConnectionFailure();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_CREATING_MSMQ_MANAGEMENT:
                    errorHandler.Handler = new CreatingMsmqManagement();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_UNEXPECTED_FAILURE_ON_CONNECT:
                    errorHandler.Handler = new UnexpectedFailureOnConnect();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_QUEUE_DOESNT_EXIST:
                    errorHandler.Handler = new QueueDoesNotExist();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_READING_QUEUE_MESSAGE_QUANTITY:
                    errorHandler.Handler = new ReadingQueueMessageQuantity();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_READING_QUEUE_SIZE:
                    errorHandler.Handler = new ReadingQueueSize();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_UNEXPECTED_FAILURE_ON_CHECK_HEALTH_STATUS:
                    errorHandler.Handler = new UnexpectedFailureOnCheckHealthStatus();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_UNEXPECTED_FAILURE_ON_THREAD_RUNNING:
                    errorHandler.Handler = new UnexpectedFailureOnThreadRunning();
                    break;
                case PSE.Framework.Messaging.MsmqReceiver.ERROR_CODE_MSMQ_CANT_READ:
                    errorHandler.Handler = new MsmqCanNotRead();
                    break;
                default:
                    errorHandler.Handler = new GenericErrorHandler();
                    break;
            }

            _traceWriter.Trace(errorHandler.HandleError());
        }

        private void _receiver_Disconnected(object sender, EventArgs e)
        {
            _traceWriter.Trace("O MsmqReceiver desconectou.");
        }

        private void _receiver_Connected(object sender, EventArgs e)
        {
            _traceWriter.Trace("O MsmqReceiver conectou.");
        }

        private void _receiver_Resumed(object sender, EventArgs e)
        {
            _traceWriter.Trace("O MsmqReceiver retomou a leitura das mensagens.");
        }

        private void _receiver_Paused(object sender, EventArgs e)
        {
            _traceWriter.Trace("O MsmqReceiver parou a leitura das mensagens.");
        }

        #endregion

        /// <summary>
        /// Pauses the MsmqReceiver object.
        /// </summary>
        public void PauseMsmqReceiver()
        {
            _receiver.PauseMsmqReceiver();
        }

        /// <summary>
        /// Resumes the MsmqReceiver object.
        /// </summary>
        public void ResumeMsmqReceiver()
        {
            _receiver.ResumeMsmqReceiver();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources.
        /// </summary>
        public void Dispose()
        {
            Tracer.RemoveInstance(TRACE_FOLDER_NAME, _traceFileName);
            Tracer.RemoveInstance(TRACE_FOLDER_NAME, _traceFileNameHealthStatus);

            if (_receiver != null)
                _receiver.Dispose();
            _receiver = null;
        }

        #endregion
    }
}
